home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
x2ftp
/
msdos
/
source
/
vr386
/
objsppt.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-01-10
|
33KB
|
1,303 lines
/* Routines to compute poly normals and bounding sphere
// Object, object list routines
// NEW VR-386 support for composite objects,
// EMM, new recoloring and so on
/* Original module written by Bernie Roehl, December 1991 */
// (Massive changes since), mostly by Dave Stampe
// All VR-386 rewrites and integer math by Dave Stampe
// This version support MAP-EMM, and allocates representation memory
// as one large block. THis requires prescanning of PLG files
/*
This code is part of the VR-386 project, created by Dave Stampe.
VR-386 is a desendent of REND386, created by Dave Stampe and
Bernie Roehl. Almost all the code has been rewritten by Dave
Stampre for VR-386.
Copyright (c) 1994 by Dave Stampe:
May be freely used to write software for release into the public domain
or for educational use; all commercial endeavours MUST contact Dave Stampe
(dstampe@psych.toronto.edu) for permission to incorporate any part of
this software or source code into their products! Usually there is no
charge for under 50-100 items for low-cost or shareware products, and terms
are reasonable. Any royalties are used for development, so equipment is
often acceptable payment.
ATTRIBUTION: If you use any part of this source code or the libraries
in your projects, you must give attribution to VR-386 and Dave Stampe,
and any other authors in your documentation, source code, and at startup
of your program. Let's keep the freeware ball rolling!
DEVELOPMENT: VR-386 is a effort to develop the process started by
REND386, improving programmer access by rewriting the code and supplying
a standard API. If you write improvements, add new functions rather
than rewriting current functions. This will make it possible to
include you improved code in the next API release. YOU can help advance
VR-386. Comments on the API are welcome.
CONTACT: dstampe@psych.toronto.edu
*/
#include <stdio.h>
#include <alloc.h>
#include <string.h>
#include <math.h>
#include <dos.h>
#include "3dstruct.h"
#include "intmath.h" /* for special sphere test */
#include "vr_api.h"
#include "segment.h" // segment operations
#include "renderer.h"
#include "xmem.h"
///////////////// OBJECT INITIALIZATION /////////////
static void find_bounding_sphere(VISOBJ *obj, REP *rep)
{
int i;
long minx, maxx, miny, maxy, minz, maxz;
long r;
accessptr(rep->polys); // make sure it's accessible
if (rep == NULL) return;
minx = maxx = rep->verts[0].x;
miny = maxy = rep->verts[0].y;
minz = maxz = rep->verts[0].z;
for (i = 1; i < rep->nverts; ++i)
{ /* find bounding cube */
if (rep->verts[i].x < minx) minx = rep->verts[i].x;
if (rep->verts[i].y < miny) miny = rep->verts[i].y;
if (rep->verts[i].z < minz) minz = rep->verts[i].z;
if (rep->verts[i].x > maxx) maxx = rep->verts[i].x;
if (rep->verts[i].y > maxy) maxy = rep->verts[i].y;
if (rep->verts[i].z > maxz) maxz = rep->verts[i].z;
}
/* compute center of cube */
obj->osphx = obj->sphx = (maxx - minx) /2 + minx;
obj->osphy = obj->sphy = (maxy - miny) /2 + miny;
obj->osphz = obj->sphz = (maxz - minz) /2 + minz;
/* farthest point from center is the radius of the bounding sphere */
r = 0;
for (i = 0; i < rep->nverts; ++i)
{
r = magnitude32((rep->verts[i].x - obj->sphx),
(rep->verts[i].y - obj->sphy),
(rep->verts[i].z - obj->sphz) );
if (r > obj->sphr) obj->sphr = r;
}
}
#define NSCALE 536870912 /* 2^29: desired magnitude of normal */
static void compute_normal(POLY *p) /* Compute a polygon's normal */
{ /* uses Dave's fast int math */
int i = 0; /* so it could be used for morphing */
int j, k;
long qinx, qiny, qinz;
long longest = 0;
long size;
VERTEX *s,*e, *ls, *le;
p->onormalx = p->normalx = NSCALE; /* default solution */
p->onormaly = p->normaly = 0;
p->onormalz = p->normalz = 0;
if (p->npoints < 3) return; /* fake it for lines */
if (p->npoints == 3) /* only one solution for triangle: ideal for morphing */
{
find_normal(p->points[0]->ox,p->points[0]->oy,p->points[0]->oz,
p->points[1]->ox,p->points[1]->oy,p->points[1]->oz,
p->points[2]->ox,p->points[2]->oy,p->points[2]->oz,
&qinx, &qiny, &qinz );
p->onormalx = p->normalx = qinx;
p->onormaly = p->normaly = qiny;
p->onormalz = p->normalz = qinz;
return;
}
/* if more sides: find longest cross product*/
for (i = 0; i < p->npoints-1; i++)
{
s = p->points[i];
e = p->points[i+1];
size = big_dist(s->ox, s->oy, s->oz, e->ox, e->oy, e->oz);
if (size > longest)
{
longest = size;
ls = s;
le = e;
}
}
s = p->points[i];
e = p->points[0];
size = big_dist(s->ox, s->oy, s->oz, e->ox, e->oy, e->oz);
if (size > longest)
{
longest = size;
ls = s;
le = e;
}
if (longest == 0) return; // degenerate poly
k = -1; /* now find acceptable point */
for (i = 0; i < p->npoints; i++)
{
s = p->points[i];
if (s != ls && s != le)
{
j = find_normal(ls->ox, ls->oy, ls->oz, le->ox, le->oy, le->oz,
s->ox, s->oy, s->oz, &qinx, &qiny, &qinz );
if (j > k) /* longer: more precision */
{
p->onormalx = p->normalx = qinx;
p->onormaly = p->normaly = qiny;
p->onormalz = p->normalz = qinz;
k = j;
}
if (j > 16) return; /* enough vertices tried already */
}
}
}
/////////////////// REPRESENTATION CONTROL ////////////////
void lock_current_representation(OBJECT *o, WORD n) // forces object to use one rep:
{ // used for morphing animation
REP *r;
if(!(o=object2visobj(o))) return; // is a segment!
if (o)
{
o->oflags |= OBJ_REPLOCK;
if(n==0) return;
r = o->replist;
while (n-- && r->next) r = r->next;
o->current_rep = r;
}
}
void unlock_current_representation(OBJECT *o) // allow rep to be selected by distance again
{
if(!(o=object2visobj(o))) return; // is a segment!
if (o) o->oflags &= ~OBJ_REPLOCK;
}
static void activate_rep(OBJECT *obj, REP *rep)
{
if(!(obj=object2visobj(obj))) return; // is a segment!
obj->current_rep = rep; // make rep the current one: used during lock
}
void select_first_representation(OBJECT *obj) // make first rep the current one: used during lock
{
if(!(obj=object2visobj(obj))) return; // is a segment!
activate_rep(obj, obj->replist);
}
// make next rep the current one: used during lock
WORD select_next_representation(OBJECT *obj) /* return 1 if wrapping back to first */
{
if(!(obj=object2visobj(obj))) return 0; // is a segment!
if (obj->current_rep == NULL)
activate_rep(obj, obj->replist);
else if ((obj->current_rep)->next == NULL)
{
activate_rep(obj, obj->replist);
return 1;
}
else
{
activate_rep(obj, (obj->current_rep)->next);
return 0;
}
return 1;
}
// find representation that will be
// active at given screen size
void select_representation(OBJECT *obj, WORD size)
{
REP *p;
if(!(obj=object2visobj(obj))) return; // is a segment!
for (p = obj->replist; p; p = p->next)
if (p->size <= size)
{
activate_rep(obj, p);
break;
}
}
// set size field of rep
void set_representation_size(OBJECT *obj, WORD size)
{
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return;
rep->size = size;
}
// read size field
WORD get_representation_size(OBJECT *obj)
{
REP *rep;
if(!(obj=object2visobj(obj))) return 0; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return 0;
return rep->size;
}
/////////////////// DELETE A REPRESENTATION ///////////////
static void del_rep(REP *rep) // delete rep
{
if (rep == NULL) return;
if (rep->polys) extfree(rep->polys); // all memory is one big block
free(rep); // free descriptor too
}
// delete current rep of object
// NOT REALLY SAFE-- LEAVE OUT OF API
void delete_current_representation(OBJECT *obj) /* needs testing */
{
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if (rep == NULL || obj->replist == NULL) return;
if (rep == obj->replist)
obj->replist = rep->next;
else
{ // close up the list!
REP *r;
for (r = obj->replist; r->next != NULL; r = r->next)
if (r->next == rep)
break;
if (r->next == NULL) return; /* wasn't in list */
r->next = rep->next;
}
del_rep(rep);
}
///////////////// REPRESENTATION CREATE AND LOAD DATA //////////
static VERTEX **vtxalloc; // used to allocate mem for vtx ptrs
// this has been reorganized to alloc one block of memory
// for everything. This makes banked memory like EMM usable
// problem is that we don't usually know the total vertices
// that each poly will have, so the new argument tpverts was added
// for this.
REP *add_representation(OBJECT *obj, WORD size, WORD nv, WORD np, WORD tpverts)
{
REP *p, *q;
POLY *pp;
VERTEX **vpp;
VERTEX *vp;
long tmpsize;
if (obj == NULL) return NULL;
if(!(obj=object2visobj(obj))) return NULL; // is a segment!
if ((p = malloc(sizeof(REP))) == NULL) return NULL;
p->size = size;
p->nverts = 0;
p->npolys = 0; /* used as counters when adding */
p->update_count = 0;
p->flags = 0;
tmpsize = nv*sizeof(VERTEX) + // vertices
tpverts*sizeof(VERTEX *) + // poly vertex counters
np*sizeof(POLY); // polys
pp = (POLY *)extmalloc(tmpsize); // also makes accessible
if(pp==NULL)
{
free(p);
return NULL;
}
vp = (VERTEX *)(pp+np);
vtxalloc = (VERTEX **)(vp+nv); // save these to alloc vertex ptrs to polys
p->polys = pp;
p->verts = vp;
activate_rep(obj, p); // make rep current so we can process it
if (obj->replist == NULL) // add to replist of object
{
obj->replist = p;
p->next = NULL;
return p;
}
q = obj->replist; // insert by switching screen size
if (q->size <= size)
{
p->next = q;
obj->replist = p;
return p;
}
while (q->next)
{
if ((q->next)->size <= size) break;
q = q->next;
}
p->next = q->next;
q->next = p;
return p;
}
// adds poly to rep: still needs vertex list defined
POLY *add_poly(OBJECT *obj, SURFACE color, WORD npoints)
{
POLY *p;
REP *rep;
if(!(obj=object2visobj(obj))) return NULL; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return NULL;
accessptr(rep->polys); // ensure access to rep data
p = &(rep->polys[rep->npolys]); // pointer to current poly (last added)
p->points = vtxalloc; // "allocate" vertex memory
vtxalloc += npoints;
p->object = obj;
p->color = color;
p->npoints = 0;
++rep->npolys;
return p;
}
// adds vertex to object: these aren't yet hooked to polys
void add_vertex(OBJECT *obj, long x, long y, long z)
{
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return;
accessptr(rep->polys);
rep->verts[rep->nverts].x = rep->verts[rep->nverts].ox = x;
rep->verts[rep->nverts].y = rep->verts[rep->nverts].oy = y;
rep->verts[rep->nverts].z = rep->verts[rep->nverts].oz = z;
++rep->nverts;
}
// connects vertices to polys thru vertex lists
void add_point(OBJECT *obj, POLY *p, WORD vertnum)
{
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return;
accessptr(rep->polys);
p->points[p->npoints++] = &(rep->verts[vertnum]);
}
///////////////// OBJECT CREATE AND SUPPORT ///////////
/* allocate an OBJECT */
// also one representation
static OBJECT *new_visobj(int nv, int np, int npverts)
{
OBJECT *obj;
if ((obj = malloc(sizeof(OBJECT))) == NULL) return NULL;
obj->oflags = IS_VISOBJ;
obj->owner = NULL;
obj->current_rep = obj->replist = NULL;
if (add_representation(obj, 0, nv, np, npverts) == NULL)
{
free(obj);
return NULL;
}
obj->sphx = obj->sphy = obj->sphz = 0;
obj->osphx = obj->osphy = obj->osphz = 0;
obj->sphr = 0;
obj->prev = NULL;
obj->nnext = NULL;
obj->update_count = 0;
return obj;
}
OBJECT *create_invisible_object()
{
return new_seg(NULL);
}
OBJECT *create_fixed_object(WORD nv, WORD np, WORD npverts)
{
return new_visobj(nv, np, npverts);
}
void delete_visobj(OBJECT *obj) // delete an object and all its reps
{
VISOBJ *v = object2visobj(obj);
REP *rep,*next;
if(!v) return;
rep = v->replist;
while (rep)
{
next = rep;
rep = next->next;
del_rep(next);
}
remove_from_objlist(v);
if(v->owner) seg_reset_object(v->owner, v);
free(v);
}
OBJECT *create_moveable_object(WORD nv, WORD np, WORD npverts)
{
SEGMENT *s;
VISOBJ *v = new_visobj(nv, np, npverts);
if(!v) return NULL;
s = new_seg(NULL);
if(!s)
{
delete_visobj(v);
return NULL;
}
seg_set_object(s, v);
return v;
}
OBJECT *make_fixed_object_moveable(OBJECT *obj, OBJECT *invis)
{
SEGMENT *s = object2segment(invis);
VISOBJ *v = object2visobj(obj);
if(!v) return NULL;
if(v->owner) return v->owner; // already moveable!
if(!s) s = new_seg(NULL); // no link supplied
if(!s)
{
delete_visobj(v);
return NULL;
}
seg_set_object(s, v);
return v;
}
// disconnects visible part of object from
// invisible object, returns visible part.
// invisible part is stored in **invis.
// <preserve> sets whether to leave fixed
// object in same world positon-- DON'T USE
// if you are going to make object moveable again!
OBJECT *make_moveable_object_fixed(OBJECT *obj, OBJECT **invis, BOOL preserve)
{
SEGMENT *s = object2segment(obj);
VISOBJ *v = object2visobj(obj);
if(invis) *invis = s;
if(!v) return NULL;
if(!s) return v;
if(preserve)
{
REP *r = v->current_rep;
REP *n = v->replist;
update_segment(s);
while(n) // convert all reps to world coords
{
v->current_rep = n;
accessptr(n->polys);
matmove_rep(n, get_seg_pmatrix(s));
copy_world_to_object(v);
n = n->next;
}
}
seg_reset_object(s,v);
return v;
}
OBJECT *convert_objlist_to_moveable_object(OBJLIST *olist)
{
VISOBJ *robj, *xobj, *obj = first_in_objlist(olist);
WORD oflags;
if(!obj) return NULL; // no objects!
if(!next_in_objlist(obj)) // only one object: make moveable
{
return make_fixed_object_moveable(obj,NULL);
}
else // many objects: join together
{
robj = create_invisible_object();
if(!robj) return NULL;
xobj = make_fixed_object_moveable(obj,NULL);
naked_attach_object(xobj, robj); // be sure NOT moved off list!
while((obj=next_in_objlist(obj))!=NULL)
{
xobj = make_fixed_object_moveable(obj,NULL);
naked_attach_object(xobj, robj); // be sure NOT moved off list!
}
}
return robj;
}
// can delete segments or visobjs, but not if
// the segment is system owned (camera, light)
void delete_object(OBJECT *obj)
{
SEGMENT *s = object2segment(obj); // get parts
VISOBJ *v = object2visobj(obj);
if(v) delete_visobj(v); // delete visobj
if(s)
if(!(is_system_owned(s))) // delete segment if possible
destroy_segment(s);
}
/////////////////// OBJECT MANIPULATION ////////////
// set object flag bits
void set_obj_flags(OBJECT *obj, unsigned int val)
{
obj->oflags = (val & OBJ_FLAG_MASK) |
(obj->oflags & (~OBJ_FLAG_MASK)) | IS_VISOBJ;
}
// read object flag bits
unsigned get_obj_flags(OBJECT *obj)
{
return obj->oflags & OBJ_FLAG_MASK;
}
// read bounding sphere of object
long get_object_bounds(OBJECT *obj, long *x, long *y, long *z)
{
if(!(obj=object2visobj(obj))) return 0; // is a segment!
if (x) *x = obj->sphx;
if (y) *y = obj->sphy;
if (z) *z = obj->sphz;
return obj->sphr;
}
// computes renderer data for object
void compute_object(OBJECT *obj) /* for current rep only! */
{
int i;
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return;
accessptr(rep->polys); // make sure data is accessible!
for (i = 0; i < rep->npolys; ++i)
compute_normal(&rep->polys[i]);
find_bounding_sphere(obj, rep);
}
// computes renderer data for object
void compute_all_object(OBJECT *obj) /* for all reps */
{
int i;
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->replist;
if (rep == NULL) return;
obj->sphr = 0;
while (rep)
{
obj->current_rep = rep;
accessptr(rep->polys);
for (i = 0; i < rep->npolys; ++i)
compute_normal(&rep->polys[i]);
find_bounding_sphere(obj, rep);
rep = rep->next;
}
}
///////////////// OBJECT LIST MANAGEMENT //////////
// NOTES ON OBJECT LIST
// Why is there an object list with a special header?
// the header serves to terminate the head of the
// double-linked object list, so that the object list
// is easier to add or delete from (the only special
// case is the header, and it just looks like another
// object with a NULL prev ptr. The double-linked list
// was introduced by Dave to allow the quick insertion
// and deletion of objects from lists, critical since
// moving objects often change object lists when splits
// are used.
// OBJECT LISTS contain only VISOBJs, and are used for
// visibility only (in split trees)
OBJLIST *new_objlist()
{
OBJLIST *p;
p = malloc(sizeof(OBJLIST)); // allocate a header
if(p)
{
p->nnext = NULL; // set to empty
p->prev = NULL;
p->oflags = OBJLIST_HEADER;
}
return p;
}
BOOL on_any_objlist(OBJECT *obj) // is it currently in a list?
{
if (obj == NULL) return FALSE;
if(!(obj=object2visobj(obj))) return FALSE; // is a segment!
return (obj->prev!=NULL);
}
// drop from list, needed often
void remove_from_objlist(OBJECT *obj)
{
if (obj == NULL) return;
if(!(obj=object2visobj(obj))) return; // is a segment!
if (obj->prev == NULL) return; // object list header, or not on objlist
if (obj->nnext != NULL) obj->nnext->prev = obj->prev;
obj->prev->nnext = obj->nnext;
obj->nnext = obj->prev = NULL;
}
// put object at head of list
// remove if already on any list
void add_to_objlist(OBJLIST *list, OBJECT *obj)
{
if (list == NULL || obj == NULL) return;
if(!(obj=object2visobj(obj))) return; // is a segment!
if(obj->prev) remove_from_objlist(obj);
if (list->nnext == NULL)
obj->nnext = NULL; /* first in empty list */
else
{
list->nnext->prev = obj; // else add before first
obj->nnext = list->nnext;
}
list->nnext = obj;
obj->prev = (OBJECT *) list;
}
void delete_objlist_and_objects(OBJLIST *list)
{
if (list) // delete list, and all objects in it
{
while (list->nnext) // does this work????
{
OBJECT *obj;
obj = list->nnext;
remove_from_objlist(list->nnext);
delete_object(obj);
}
if(!(list->oflags&SYSTEM_OWNED)) free(list);
}
}
void delete_objlist_keeping_objects(OBJLIST *list)
{
if (list) // delete list, and all objects in it
{
while (list->nnext) // does this work????
{
remove_from_objlist(list->nnext);
}
if(!(list->oflags&SYSTEM_OWNED)) free(list);
}
}
// removes all from <from>, adds to start of <to>
void merge_objlists(OBJLIST *from, OBJLIST *to)
{
VISOBJ *vt = to, *vf = from;
if(!vf->nnext) return; // none to move
if(!vt->nnext) // empty to list
{
vt->nnext = vf->nnext; // just move it all
vf->nnext->prev = vt;
vf->nnext = NULL;
}
while(vf->nnext) vf = vf->nnext; // find end of list
vt->nnext->prev = vf; // splice lists
vf->nnext = vt->nnext;
vf->nnext->prev = vt;
vf->nnext = NULL;
}
OBJLIST *which_objlist(OBJECT *obj) // look for head of list object is
{ // in. Kinda slow for big lists
if (obj == NULL) return NULL;
if(!(obj=object2visobj(obj))) return NULL; // is a segment!
for (obj = obj->prev; obj->prev; obj = obj->prev);
return (OBJLIST *) obj;
}
/* Additional functions suggested by wendellj@microsoft.com */
OBJECT *first_in_objlist(OBJLIST *objlist)
{
return objlist->nnext;
}
OBJECT *next_in_objlist(OBJECT *obj)
{
if(!(obj=object2visobj(obj))) return NULL; // is a segment!
return obj->nnext;
}
OBJECT *prev_in_objlist(OBJECT *obj)
{
if(!(obj=object2visobj(obj))) return NULL; // is a segment!
return obj->prev;
}
BOOL is_first_in_objlist(OBJECT *obj)
{
if(!(obj=object2visobj(obj))) return FALSE; // is a segment!
return obj->prev->prev == NULL;
}
BOOL is_last_in_objlist(OBJECT *obj)
{
if(!(obj=object2visobj(obj))) return FALSE; // is a segment!
return obj->nnext == NULL;
}
// total number of vertices of all polys NEW NEW NEW
WORD total_object_pverts(OBJECT *obj)
{
int i,j;
REP *rep;
if(!(obj=object2visobj(obj))) return 0; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return 0;
accessptr(rep->polys);
j = 0;
for(i=0;i<rep->npolys;i++)
j += rep->polys[i].npoints;
return j;
}
// current rep info
void get_obj_info(OBJECT *obj, int *nv, int *np)
{
REP *rep;
VISOBJ *v;
if(!(v=object2visobj(obj))) return; // is a segment!
rep = v->current_rep;
if (rep == NULL) return;
if (nv) *nv = rep->nverts;
if (np) *np = rep->npolys;
}
// This is another useful function.
// You can use it to perform an
// operation on every object in a
// list with one call. Speed? What speed?
// this is useful with splits for walking
// all visible objects in the environment
void walk_objlist(OBJLIST *objlist, void (*fn)(OBJECT *))
{
OBJECT *obj;
OBJECT *next;
/* DS fix: so objects may be deleted by the fn() ! */
if (objlist == NULL) return;
obj = objlist->nnext;
while (obj != NULL)
{
next = obj->nnext; /* get ahead of time in case object is modified */
fn(obj);
obj = next;
}
}
void get_vertex_world_info(OBJECT *obj, WORD vertnum, COORD *x, COORD *y, COORD *z)
{
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return;
accessptr(rep->polys); // make sure we can acceess data
if (x) *x = rep->verts[vertnum].x;
if (y) *y = rep->verts[vertnum].y;
if (z) *z = rep->verts[vertnum].z;
}
void get_vertex_info(OBJECT *obj, WORD vertnum, COORD *x, COORD *y, COORD *z)
{
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return;
accessptr(rep->polys); // make sure we can acceess data
if (x) *x = rep->verts[vertnum].ox;
if (y) *y = rep->verts[vertnum].oy;
if (z) *z = rep->verts[vertnum].oz;
}
void set_vertex_coords(OBJECT *obj, int vertnum, long x, long y, long z)
{
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return;
accessptr(rep->polys); // make sure we can acceess data
rep->verts[vertnum].ox = rep->verts[vertnum].x = x;
rep->verts[vertnum].oy = rep->verts[vertnum].y = y;
rep->verts[vertnum].oz = rep->verts[vertnum].z = z;
}
WORD get_poly_vertex_index(OBJECT *obj, WORD poly, WORD vertex)
{
int i, n;
REP *rep;
if(!(obj=object2visobj(obj))) return 0; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return 0;
accessptr(rep->polys); // make sure we can acceess data
return (FP_OFF(rep->polys[poly].points[vertex]) -
FP_OFF(rep->verts)) /sizeof(VERTEX);
}
// get data on poly
void get_poly_info(OBJECT *obj, WORD polynum, SURFACE *color, WORD *nverts)
{
int i, n;
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return;
accessptr(rep->polys); // make sure we can acceess data
if (color) *color = rep->polys[polynum].color;
n = rep->polys[polynum].npoints;
if (nverts) *nverts = n;
}
void set_poly_color(OBJECT *obj, WORD polynum, SURFACE color)
{
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return;
accessptr(rep->polys); // make sure we can acceess data
if (polynum < rep->npolys)
rep->polys[polynum].color = color;
}
/////// OBJECT RECOLORING
// very flexible remaps colors of object
void masked_remap_object_surface(OBJECT *obj, SURFACEPAIR map[20], WORD nmap,
SURFACE sbitmask, SURFACE dbitmask, BOOL all)
{
REP *rep;
int i,j;
SURFACE color;
if(!(obj=object2visobj(obj))) return; // is a segment!
if (all) select_first_representation(obj);
do {
rep = obj->current_rep;
accessptr(rep->polys);
for(i=0;i<rep->npolys;i++)
{
color = rep->polys[i].color;
for(j=0;j<nmap;j++)
{
if((color&sbitmask)==(map[j].old&sbitmask))
{
color = (color&(~dbitmask))|(map[j].new&dbitmask);
break;
}
}
rep->polys[i].color = color;
}
} while(all && !select_next_representation(obj));
}
// flexible recolo/resurface object
void masked_recolor_object_surface(OBJECT *obj, SURFACE new,
SURFACE bitmask, BOOL all)
{
REP *rep;
int i;
SURFACE color;
if(!(obj=object2visobj(obj))) return; // is a segment!
if (all) select_first_representation(obj);
do {
rep = obj->current_rep;
accessptr(rep->polys);
for(i=0;i<rep->npolys;i++)
{
color = rep->polys[i].color;
color = (color&(~bitmask))|(new&bitmask);
rep->polys[i].color = color;
}
} while(all && !select_next_representation(obj));
}
// very flexible remaps colors
// does for all objects in objlist
void objlist_remap_surface(OBJLIST *olist, SURFACEPAIR map[20], WORD nmaps,
SURFACE sbitmask, SURFACE dbitmask)
{
OBJECT *obj = first_in_objlist(olist);
if(nmaps && obj)
{
masked_remap_object_surface(obj, map, nmaps, sbitmask, dbitmask, 0);
while((obj=next_in_objlist(obj))!=NULL)
masked_remap_object_surface(obj, map, nmaps, sbitmask, dbitmask, 0);
}
}
/* find object that point is closest to center of */
/* this is used with the Powerglove to select objects */
OBJECT *best_collision(OBJLIST *objlist, long x, long y, long z)
{
OBJECT *obj;
OBJECT *bestobj = NULL;
long mindist = 0x7FFFFFFF;
long dist;
if (objlist == NULL) return NULL;
for (obj = objlist->nnext; obj; obj = obj->nnext)
{ /* not selectable... skip */
if (obj->oflags & (OBJ_INVIS|OBJ_NONSEL)) continue;
if (obj->owner == NULL) continue; /* ignore if no segment */
dist = sphere_pretest(obj, x, y, z); /* test object */
if (dist < mindist)
{
mindist = dist; /* better fit: accept */
bestobj = obj;
}
}
return bestobj;
}
// set highlight flags in object
void highlight_object(OBJECT *obj)
{
int i;
if (obj == NULL) return;
if(!(obj=object2visobj(obj))) return; // is a segment!
obj->oflags |= OBJ_HIGHLIGHTED;
}
void unhighlight_object(OBJECT *obj)
{
int i;
if (obj == NULL) return;
if(!(obj=object2visobj(obj))) return; // is a segment!
obj->oflags &= ~OBJ_HIGHLIGHTED;
}
void make_object_unselectable(OBJECT *obj)
{
int i;
if (obj == NULL) return;
if(!(obj=object2visobj(obj))) return; // is a segment!
obj->oflags |= OBJ_NONSEL;
}
void mark_object_invisible(OBJECT *obj)
{
int i;
if (obj == NULL) return;
if(!(obj=object2visobj(obj))) return; // is a segment!
obj->oflags |= OBJ_INVIS;
}
void mark_object_visible(OBJECT *obj)
{
int i;
if (obj == NULL) return;
if(!(obj=object2visobj(obj))) return; // is a segment!
obj->oflags &= ~OBJ_INVIS;
}
// set/clear flags in visobj part of object
void set_object_flags(OBJECT *obj, WORD flagmask, WORD val)
{
if(!(obj=object2visobj(obj))) return; // is a segment!
if(val) obj->oflags |= flagmask;
else obj->oflags &= ~flagmask;
}
///////////////////// SCREEN POINT MONITOR SUPPORT //////////////
// NOTE: the vertex returned by these (and the screen monitor) is
// the sequential vertex IN THE POLY, NOT the object vertex index.
// see what object's on the screen
// at the given point <in cursor2d.c>
extern OBJECT *find_object_on_screen(WORD x, WORD y);
// ASSUMES YOU'VE USED DONE A REFRESH WITH MONITOR
static SWORD screen_obj_poly; // index of poly in object (-1 if none)
static SWORD screen_obj_vert; // index of vertex in object (-1 if none)
static SWORD screen_poly_vert; // index of vertex in poly (-1 if none)
void process_screen_monitor()
{
OBJECT *obj;
POLY *p;
int pi;
screen_obj_poly = screen_obj_vert = screen_poly_vert = -1;
obj = screen_monitor_object();
if (obj)
{
p = screen_monitor_poly();
if (p == NULL) return;
screen_obj_poly = (FP_OFF(p) - FP_OFF(obj->current_rep->polys)) /sizeof(POLY);
screen_poly_vert = screen_monitor_vertex();
if(screen_poly_vert!=-1)
screen_obj_vert = get_poly_vertex_index(obj, screen_obj_poly,
screen_poly_vert);
}
}
OBJECT *screen_found_object()
{
return screen_monitor_object();
}
SWORD screen_found_poly()
{
return screen_obj_poly;
}
SWORD screen_found_poly_vertex()
{
return screen_poly_vert;
}
SWORD screen_found_object_vertex()
{
return screen_obj_vert;
}
////////////////// MORE DATA-HIDING ROUTINES //////////
SEGMENT *object2segment(OBJECT *obj)
{
if(!obj) return NULL;
if(is_object_segment(obj)) return obj;
return ((VISOBJ *)obj)->owner;
}
unsigned get_object_sorting(OBJECT *obj)
{
if(!(obj=object2visobj(obj))) return 0; // is a segment!
return obj->oflags & OBJ_DEPTH_MASK;
}
void set_object_sorting(OBJECT *obj, unsigned depth_type)
{
if(!(obj=object2visobj(obj))) return; // is a segment!
obj->oflags = (depth_type & OBJ_DEPTH_MASK)
| (obj->oflags & (~OBJ_DEPTH_MASK))
| IS_VISOBJ;
}
/************** MOVE OBJECT COMPLETELY ***********/
void copy_world_to_object(OBJECT *obj) // used to "lock" fixed objects
{ // on loading
int i;
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return;
accessptr(rep->polys);
for (i = 0; i < rep->nverts; ++i)
{
rep->verts[i].ox = rep->verts[i].x;
rep->verts[i].oy = rep->verts[i].y;
rep->verts[i].oz = rep->verts[i].z;
}
}
void scale_object(OBJECT *obj, float sx, float sy, float sz)
{
int i;
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if (rep == NULL) return;
accessptr(rep->polys);
for (i = 0; i < rep->nverts; ++i)
{
rep->verts[i].x = rep->verts[i].ox = rep->verts[i].ox*sx;
rep->verts[i].y = rep->verts[i].oy = rep->verts[i].oy*sy;
rep->verts[i].z = rep->verts[i].oz = rep->verts[i].oz*sz;
}
}
void apply_matrix(OBJECT *obj, MATRIX m) // moves object completely
{ // useful on loading
REP *rep;
if(!(obj=object2visobj(obj))) return; // is a segment!
rep = obj->current_rep;
if(rep==NULL) return;
accessptr(rep->polys);
matmove_osphere(obj, m);
matmove_rep(rep, m);
}
void move_rep(VISOBJ *obj) /* move current rep of object */
{ /* called from renderer itself */
REP *rep = obj->current_rep;
SEGMENT *s = obj->owner;
accessptr(rep->polys);
if(s) matmove_rep(rep, get_seg_pmatrix(s));
rep->update_count = obj->update_count; // mark as current
}